package com.md.config.database.slave;

import com.md.value.server.DataSourceContextHolder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.InvocationTargetException;

/**
 * @author 刘骄阳
 * 2023-12-20 15:41
 */
@AllArgsConstructor
@Slf4j
@Intercepts({
  /*
   * 这个方法在执行更新操作时调用，用于处理更新操作的 SQL 执行。
   */
  @Signature(
    type = Executor.class,
    method = "update",
    args = {MappedStatement.class, Object.class}
  ),
  /*
   * 这些方法分别在执行查询操作时调用，用于处理查询操作的 SQL 执行。
   * Executor.query() 方法根据重载版本不同会有不同的参数，在执行查询时会传入相应的参数，如 MappedStatement、Object（查询参数）、RowBounds、ResultHandler 等
   */
  @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
  ),
  @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,
      BoundSql.class}
  )})
public class SlaveInterceptor implements Interceptor {

  @Override
  public Object intercept(final Invocation invocation) throws InvocationTargetException,
    IllegalAccessException {
    Object target = invocation.getTarget();
    Object[] args = invocation.getArgs();
    if (target instanceof Executor) {
      MappedStatement ms = (MappedStatement) args[0];
      if (ms.getSqlCommandType() == SqlCommandType.SELECT) {
        // 切换到读取数据源
        DataSourceContextHolder.read();
      } else {
        // 切换到写入数据源
        DataSourceContextHolder.write();
      }

      // 执行原始操作，不处理具体的SQL执行逻辑
      return invocation.proceed();
    }
    return invocation.proceed();
  }
}
